package com.redxiii.util.cli;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;



/**
 * Command Line - IO
 * http://stackoverflow.com/questions/804951/is-it-possible-to-read-from-a-java-inputstream-with-a-timeout
 */
public class CmdLineIO {

	private final static AtomicInteger id = new AtomicInteger();
	
	static {
		String newline = System.getProperty("line.separator");
		newline.toCharArray();
	}
	private long 	timeout = 0;
	private boolean	escOnEmpty = true;	// Escape with empty	
	private char	ending = ':';
	private SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
	
	public static void main(String[] args) {
		CmdLineIO io = new CmdLineIO();
		io.timeout = 5000;
		String text = io.getString("Nome");
		
		System.out.println("Typed: '" + text + "'");
	}
	
	public void setEscOnEmpty(boolean escOnEmpty) {
		this.escOnEmpty = escOnEmpty;
	}
	public void setTimeout(long timeout) {
		this.timeout = timeout;
	}
	
	public String getString(String message) {
		System.out.print(message);
		System.out.print(ending);
		
		return readString();
	}
	
	public Long getLong(String message) {
		while (true) {
			String number = getString(message);
			if (number == null || number.length() == 0)
				return null;
			try {
				return Long.parseLong(number);
			} catch (NumberFormatException e) {
				System.err.println("Number format exception: " + e.getMessage());
			}
		}
	}
	
	public Integer getInteger(String message) {
		while (true) {
			String number = getString(message);
			if (number == null || number.length() == 0)
				return null;
			try {
				return Integer.parseInt(number);
			} catch (NumberFormatException e) {
				System.err.println("Number format exception: " + e.getMessage());
			}
		}
	}
	
	public Double getDecimal(String message) {
		while (true) {
			String number = getString(message);
			if (number == null || number.length() == 0)
				return null;
			try {
				return Double.parseDouble(number);
			} catch (NumberFormatException e) {
				System.err.println("Number format exception: " + e.getMessage());
			}
		}
	}
	
	
	public Date getDate(String message) {
		while (true) {
			String date = getString(message);
			if (date == null || date.length() == 0)
				return null;
			try {
				return sdf.parse(date);
			} catch (ParseException e) {
				System.err.println("Invalid date: " + e.getMessage());
			}
		}
	}
	
	public Enum<?> getEnum(Class<? extends Enum<?>> enumClass, String message)  {
		while(true) {
			String value = getString(message);
			if (value == null || value.length() == 0)
				return null;
			
			Enum<?>[] contraints = enumClass.getEnumConstants();
			StringBuilder options = new StringBuilder();
			for (Enum<?> e : contraints) {
				options.append(e.name()).append(",");
				if (e.name().equals(value))
					return e;
			}
			options.append("'blank'");
					
			System.err.println("Invalid option. Options: " + options);
		}
	}
	
	public boolean getBoolean(String message, boolean onEmpty) {
		String value = getString(message);
		if (value == null || value.length() == 0)
			return onEmpty;
		
		if (value.equalsIgnoreCase("s"))
			return Boolean.TRUE;
		else
			return Boolean.FALSE;
	}
	
	public String readString() {
		
		CmdLineReader lineReader = new CmdLineReader();
		
		while (lineReader.stream.length() == 0) {
			if (timeout > 0) {
				Thread thread = new Thread(lineReader,"CommandLineReader-" + id.getAndIncrement());
				thread.start();
				
				try {
					thread.join(timeout);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				thread.interrupt();
			} else {
				lineReader.run();
			}
			
			if (escOnEmpty)
				break;
		}
		
		return lineReader.stream.toString();
	}
}